import axios ,{AxiosRequestConfig} from 'axios'
import {useState, useRef, useCallback,useEffect} from 'react'
import { useNavigate } from 'react-router-dom';
import { message } from '../utils/message';
// <T> 表示这个方法接收外部传递过来的一个类型
/**
 * 重点：
 * 1.2、T 就变成了传递进来的ResponseType(相当于实参)
 *      T 相当于(行参）
 */
// 默认请求数据
const defaultRequestConfig = {
    url: '/',
    method: 'GET',
    data:{},
    params: {}
}
// 意思是调请求的时候可以带一个manual参数boolean
// manual:true 告诉useRequest 手动触发请求
function useRequest<T>(options: AxiosRequestConfig & {manual?: boolean} = defaultRequestConfig){
    const navigate = useNavigate(); 
    // 发送请求相关数据定义
    // 请求接收到的内容
    // 1.3 data的类型定义为ResponseType | null
    const [data, setData] = useState<T | null>(null); // data的值可以是null也可以是外部传递过来的T的类型值
    // 请求错误变量
    const [error, setError] = useState('')
    // 请求结束变量
    const [loaded, setLoaded] = useState(false)
    const controllerRef = useRef(new AbortController()) // 解除请求的api创建的实例// 它的改变不需要重新渲染页面
    // 取消请求的方法（有定义请求的方法，就有取消请求的方法）
    const cancel = ()=>{
        controllerRef.current.abort()
    }
    const request = useCallback((requestOptions: AxiosRequestConfig)=>{ // requestOptions可传可不穿，传就用内部的，不传就用外部传过来的
        // 每次请求前把请求状态和数据清空一下
        setData(null);
        setError('');
        setLoaded(false)
        
        const loginToken = localStorage.getItem('token');
        const headers = loginToken ? {token: loginToken} : {};
        
        // 发送请求
        // useRequest把<T>向下传递到axios.request<T>
        // 这样axios.request就会知道你返回的值，对应的data里的内容
        // 就是<T>类型对应的内容
        return axios.request<T>({
            baseURL: 'http://statics.dell-lee.com/shopping-project/mock/',// 正常访问路径
            url: requestOptions?.url,  // 本地访问路径
            method: requestOptions?.method, 
            signal: controllerRef.current.signal,
            data: requestOptions?.data , // 请求的内容有哪些
            params: requestOptions?.params,
            headers // 发请求时携带登陆token给后端，有时候后端会希望我们把请求登陆的token塞到header里面去,获取方法见32行
            /**
             * 这样如果未来用useRequest发请求的时候，如果已经登陆过的话，
             * 你登陆的token就会被带进请求的header中，方便后端去做鉴权
             * 但是，token不是永远都有效的，它有有效期，如果你发token的时候，它正好失效了，
             * 一般来说这种情况，虽然带了token给后端，但是后端会判断token是否过期，如果过期他们会给你做清空，
             * 让你做重新登陆，这种后端会抛一个403的错，表示没有权限，前端要做相应的处理，见55行。
             */
        }).then(response => {
            // const a = response.data; // axios.request发送完请求后，它返回的response.data的类型就是上面传过来的<T>的类型
            setData(response.data)
            return response.data;
        }).catch((e: any)=>{
            if(e?.response?.status === 403){
                localStorage.removeItem('token') // 去除之前过期的token，然后做页面跳转
                navigate('/login');
            }
            setError(e.message || 'unknown request error')
            throw new Error(e)
        }).finally(()=>{
            setLoaded(true);
        })
    }, [navigate]);
    useEffect(()=>{
        if(!options.manual){ //希望自动的时候才发请求
            request(options).catch((e)=>{
                message(e?.message) 
            })
        }
      }, [options, request])
    // 1.4 把data返回，返回data的类型一定为ResponseType | null
    // console.log('[data]',data)
    return {data, error, loaded, request, cancel} // 因为这里把data return出去了，那么这个data 的类型是 Login index.tsx第十行useRequest<ResponseType>中的ResponseType 的类型
}
export default useRequest;